iT邦幫忙

2026 iThome 鐵人賽

DAY 19
0
Software Development

文藝復興:這段程式碼,好像有點味道系列 第 19

Day 19|畫框上寫著「這裡應該是一朵花」:註解 (Comments)

  • 分享至 

  • xImage
  •  

李昂・巴蒂斯塔・阿伯提(Leon Battista Alberti)在《論建築》裡提出 concinnitas
各部分之間的和諧比例,並給「美」下了近乎嚴苛的定義:

「美,是所有部分之間的和諧與一致,達到一種境界——多加一分是累贅,少了一分是缺憾

模組一到模組三,處理的都是「結構性」問題
模組四不一樣,處理的是「贅肉」,那些不影響功能,卻默默增加維護成本的多餘之物

六個「贅肉」警訊:

Day Code Smell 一句話定位
19 註解 (Comments) 用一段解釋,掩蓋一段本該寫清楚的程式碼
20 重複的程式碼 (Duplicate Code) 同一段邏輯,在系統裡被抄寫了不只一次
21 懶惰的類別 (Lazy Class) 存在的價值,撐不起維護它的成本
22 純資料類別 (Data Class) 只會被別人擠資料,自己什麼都不做
23 無用的程式碼 (Dead Code) 沒有人呼叫,卻沒有人敢刪的舊程式碼
24 猜測性通用 (Speculative Generality) 為了「以後可能會用到」預先蓋好的空彈性

接下來六天,練習的不是「加什麼」,是 「什麼可以拿掉」

第一站,從最容易被誤解的一種開始

「註解」


一幅畫,如果畫得夠好,不需要在畫框上貼一張紙條寫「這裡是一朵花」

觀者一眼就看得出來,那是一朵花
因為花瓣的形狀、顏色的層次、光影的處理,已經把「這是花」這件事,說得很清楚了

如果一幅畫,必須靠貼紙條才能讓人看懂畫的是什麼
問題不在紙條寫得好不好,是那幅畫,本來就沒畫清楚

一段免運判斷,配上三行說明

團隊要加一個「免運門檻」判斷,寫出來的第一版是這樣:

public bool IsEligibleForFreeShipping(OrderRequest request)
{
    // 如果是 VIP 客戶,直接享有免運
    // 如果訂單金額超過 1000 元,也享有免運
    // 但如果是超商取貨,門檻降到 500 元,且不能是易碎品
    if (request.Tier == CustomerTier.Vip ||
        request.Line.Qty * 100 >= 1000 ||
        (request.DeliveryMethod == "超商" && request.Line.Qty * 100 >= 500 && !request.IsFragile))
    {
        return true;
    }

    return false;
}

三行註解,對應著三條規則
看起來很貼心不懂的人,讀註解就知道邏輯是什麼

貼心的紙條,遲早會撕破

半年後,業務調整規則:超商取貨的免運門檻,從 500 元調成 700 元

工程師改了程式碼裡的 500,改成 700

改完程式碼,順手忘記改註解,註解上還寫著「門檻降到 500 元」

三個月後,另一位工程師讀到這段程式碼,先看了註解,以為門檻是 500,改了另一段依賴這個門檻的邏輯

結果兩處數字對不上,產生了一個要花一個下午才查得出來的 Bug

這正是註解最危險的地方
「程式碼會被執行,註解不會。程式碼錯了,測試會抓到;註解錯了,沒有任何機制會提醒你」

讓 if 條件,自己說清楚是什麼

真正的問題,不是「該不該寫註解」,是這段判斷式,本來就沒有把自己的意圖說清楚

解法是提煉變數 (Extract Variable):把每一段條件,命名成一個有意義的布林變數,讓變數名稱取代原本的註解

public bool IsEligibleForFreeShipping(OrderRequest request)
{
    bool isVip = request.Tier == CustomerTier.Vip;
    bool meetsStandardThreshold = request.Line.Qty * 100 >= 1000;
    bool meetsConvenienceStoreThreshold =
        request.DeliveryMethod == "超商" &&
        request.Line.Qty * 100 >= 700 &&
        !request.IsFragile;

    return isVip || meetsStandardThreshold || meetsConvenienceStoreThreshold;
}

三個變數名稱,就是三條規則的說明書

門檻從 500 改成 700,只需要改一個地方,不會有「程式碼跟註解對不上」的風險
因為現在只有一份真相,不是程式碼加上一份平行的文字說明

什麼時候,註解真的該留著

阿伯提的節制美學,不是「所有裝飾都不該有」,是每一個留下的裝飾,都該有不可替代的理由

註解也一樣,有幾種情況,它是真正必要的:

  • 解釋「為什麼」,而不是「做什麼」
    • 例如「這裡用 0.99 而不是 1.0,是為了繞開第三方金流 SDK 的一個已知捨入誤差」
    • 這種脈絡,寫在程式碼結構裡很難表達,需要文字說明
  • 解釋一段公開的複雜演算法
    • 像加密演算法、金融公式,程式碼本身已經是最簡潔的表達,但背後的理論依據,值得用註解交代
  • API 文件
    • 給外部使用者看的公開介面說明,是必要的契約,不是壞味道

「做什麼」的註解該被消滅;「為什麼」的註解,才值得留下

自我檢查清單

  1. 這段註解,是不是在複述程式碼本身已經做的事?
  2. 如果把這段程式碼改一個更好的名字,還需要這段註解嗎?
  3. 這段註解上次更新,是什麼時候?跟現在的程式碼,還對得上嗎?
  4. 這段註解,解釋的是「做什麼」,還是「為什麼要這樣做」?
  5. 如果拿掉這段註解,光靠程式碼本身,讀者還看得懂意圖嗎?

明日預告

明天我們看一種更直接的贅肉:同一段邏輯,被複製貼上到系統的好幾個角落,各自過著自己的人生

模組四第二站:重複的程式碼(Duplicate Code)


上一篇
Day 18|灰泥要乾之前:現在修,還是先記一筆技術債?
下一篇
Day 20|同一張草稿,抄了五份放在不同抽屜:重複的程式碼 (Duplicate Code)
系列文
文藝復興:這段程式碼,好像有點味道27
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言